package cn.bluethink.eguan.exception;

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import cn.bluethink.eguan.result.ResponseMessage;
import cn.bluethink.eguan.result.ResponseResult;

/**
 * 全局异常处理
 * 
 * @title 苏州中科蓝迪公司所有(c)2016-2021
 * @version 0.1.0
 * @describe 
 * @author liuxn
 * @date  2018年8月16日
 */
@RestControllerAdvice
public class GlobalException {

    private static Logger logger = LoggerFactory.getLogger(GlobalException.class);

    @ExceptionHandler
    public ResponseResult<?> handle(HttpServletRequest request, HttpServletResponse response, Exception ex) {
    	
    	// @Valid 捕获BindException
    	if(ex instanceof BindException) {
			return handleBindException(request, response, ex);
    	} 
    	
    	/**
    	 * 存在外键关联时，捕获异常
    	 * 违反数据完整性约束
    	 */
    	if(ex instanceof DataIntegrityViolationException) {
        	return new ResponseResult<>(400, "操作失败（违反数据完整性约束）");
    	}
    	
    	// 其他异常
        return handleInternalExp(request, response, ex);
    }
    
    /**
     * valid 校验
     * @param request
     * @param response
     * @param ex
     * @return
     */
	private ResponseResult<?> handleBindException(HttpServletRequest request, HttpServletResponse response, Exception ex) {
		BindException bindException = (BindException) ex;
		BindingResult bindResult = bindException.getBindingResult();
		
		String message = "请求参数不完整！";
		
		List<Map<String, String>> errors = new ArrayList<Map<String,String>>();
		
		for(int i=0; i<bindResult.getAllErrors().size(); i++){
			
			Map<String, String> error = new HashMap<String, String>();
			
			DefaultMessageSourceResolvable dm = (DefaultMessageSourceResolvable) bindResult.getAllErrors().get(i).getArguments()[0];

			String field = dm.getDefaultMessage();
			String errorMessage = bindResult.getAllErrors().get(i).getDefaultMessage();
			
			logger.info("field: ", field);
			logger.info("message: ", errorMessage);
			
			error.put("field", field);
			error.put("message", errorMessage);
			errors.add(error);
			
		}
		
		return new ResponseResult<List<Map<String, String>>>(ResponseMessage.UN_ACCEPT.getStatus(), message, errors);
	}

	private ResponseResult<?> handleInternalExp(HttpServletRequest request, HttpServletResponse response, Exception ex) {
		String path = request.getRequestURI();
        String method = request.getMethod();

        logger.info("Request URI:{}", path);
        logger.info("Request Method:{}", method);

        String params = "";
        StringBuffer sb = new StringBuffer();
        Enumeration<String> parameterNames = request.getParameterNames();
        while (parameterNames.hasMoreElements()) {
            String key = parameterNames.nextElement();
            sb.append(key + "=" + request.getParameter(key) + "&");
        }
        if(sb!=null && sb.length()>0){
            params = sb.substring(0, sb.length()-1);
        }

        logger.info("Request Parameters:{}", params);
        logger.error("Exception Details:", ex);

        StackTraceElement[]traceElements = ex.getStackTrace();
        String[] exceptions = new String[traceElements.length];
        for(int i=0;i<traceElements.length;i++){
            exceptions[i]=traceElements[i].toString();
        }

        Map<String, Object> map = new HashMap<String, Object>(4);
        map.put("exceptions", exceptions);
        map.put("path", path);
        map.put("method", method);
        map.put("params", params);

        return new ResponseResult<Map<String, Object>>(ResponseMessage.INTERNAL_SERVER_ERROR.getStatus(), ex.getMessage(), map);
	}
}
